#include "free_egui.h"

#include <stdlib.h>
#include <string.h>
#include <math.h>

void egui_get_char_bitmap(const char *ch, int font_size, Bitmap **out_bmp)
{
	extern const uint8_t font_ascii_6x8[][6];
	extern const uint8_t font_ascii_8x16[][16];

	static uint8_t buf[32 * 64 / 8];
	static Bitmap bmp = {
		.data = buf,
		.mode = BMP_MODE_MONO,
	};
	*out_bmp = &bmp;

	if (font_size == 16)
	{
		if (*ch & 0x80)
		{
			bmp.width = 16, bmp.height = 16;
		}
		else
		{
			bmp.width = 8, bmp.height = 16;
			memcpy(bmp.data, font_ascii_8x16[*ch - ' '], 16);
		}
	}
	else if (font_size == 12)
	{
		if (*ch & 0x80)
		{
			bmp.width = 12, bmp.height = 12;
		}
		else
		{
			bmp.width = 6, bmp.height = 12;
			memcpy(bmp.data, font_ascii_6x8[*ch - ' '], 6);
		}
	}
	else
	{
		uint8_t buf0[16 * 16 / 8];
		Bitmap bmp0 = {
			.data = buf0,
			.mode = BMP_MODE_MONO,
		};
		bmp0.width = 8, bmp0.height = 16;
		memcpy(bmp0.data, font_ascii_8x16[*ch - ' '], 16);

		egui_cal_scale(&bmp0, *out_bmp, (float)font_size / 16);
	}
}

#define egui_check_rect(x, y, w, h) \
	{                               \
		if (w < 0)                  \
		{                           \
			x += w;                 \
			w = -w;                 \
		}                           \
		if (h < 0)                  \
		{                           \
			y += h;                 \
			h = -h;                 \
		}                           \
	}

static void egui_draw_pixel_mono(Bitmap *bmp, int x, int y, bool color)
{
	if (color)
		((uint8_t *)bmp->data)[(y / 8 * bmp->width) + x] |= 1 << (y % 8);
	else
		((uint8_t *)bmp->data)[(y / 8 * bmp->width) + x] &= ~(1 << (y % 8));
}

static bool egui_read_pixel_mono(Bitmap *bmp, int x, int y)
{
	return (((uint8_t *)bmp->data)[(y / 8 * bmp->width) + x] & (1 << (y % 8))) ? true : false;
}

static void egui_draw_pixel_rgb565(Bitmap *bmp, int x, int y, uint16_t color)
{
	((uint16_t *)bmp->data)[(y * bmp->width) + x] = color;
}

static uint16_t egui_read_pixel_rgb565(Bitmap *bmp, int x, int y)
{
	return ((uint16_t *)bmp->data)[(y * bmp->width) + x];
}

void egui_clear(Bitmap *bmp)
{
	if (bmp->mode == BMP_MODE_MONO)
	{
		memset(bmp->data, 0, bmp->width * (bmp->height) / 8);
	}
	else
	{
		for (int x = 0; x < bmp->width; x++)
		{
			for (int y = 0; y < bmp->height; y++)
			{
				egui_draw_pixel_rgb565(bmp, x, y, 0);
			}
		}
	}
}

void egui_draw_pixel(Bitmap *bmp, int16_t x, int16_t y, uint32_t color)
{
	if (x < 0 || x >= bmp->width || y < 0 || y >= bmp->height)
	{
		return;
	}

	if (bmp->mode == BMP_MODE_MONO)
	{
		egui_draw_pixel_mono(bmp, x, y, color);
	}
	else
	{
		egui_draw_pixel_rgb565(bmp, x, y, color);
	}
}

uint32_t egui_read_pixel(Bitmap *bmp, int16_t x, int16_t y)
{
	if (x < 0 || x >= bmp->width || y < 0 || y >= bmp->height)
	{
		return 0;
	}

	if (bmp->mode == BMP_MODE_MONO)
	{
		return egui_read_pixel_mono(bmp, x, y);
	}
	else
	{
		return egui_read_pixel_rgb565(bmp, x, y);
	}
}

void egui_inverse_pixel(Bitmap *bmp, int16_t x, int16_t y)
{
	if (x < 0 || x >= bmp->width || y < 0 || y >= bmp->height)
	{
		return;
	}

	if (bmp->mode == BMP_MODE_MONO)
	{
		egui_draw_pixel_mono(bmp, x, y, !egui_read_pixel_mono(bmp, x, y));
	}
	else
	{
		egui_draw_pixel_rgb565(bmp, x, y, ~egui_read_pixel_rgb565(bmp, x, y));
	}
}

void egui_draw_line(Bitmap *bmp, int16_t x0, int16_t y0, int16_t x1, int16_t y1, uint32_t color)
{
	int16_t dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
	int16_t dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
	int16_t err = dx + dy, e2;
	for (;;)
	{
		egui_draw_pixel(bmp, x0, y0, color);
		if (x0 == x1 && y0 == y1)
			break;
		e2 = 2 * err;
		if (e2 >= dy)
		{
			err += dy;
			x0 += sx;
		}
		if (e2 <= dx)
		{
			err += dx;
			y0 += sy;
		}
	}
}

void egui_draw_rect(Bitmap *bmp, int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color)
{
	egui_check_rect(x, y, w, h);

	for (int16_t xi = x; xi < x + w; xi++)
	{
		egui_draw_pixel(bmp, xi, y, color);
		egui_draw_pixel(bmp, xi, y + h - 1, color);
	}
	for (int16_t yi = y; yi < y + h; yi++)
	{
		egui_draw_pixel(bmp, x, yi, color);
		egui_draw_pixel(bmp, x + w - 1, yi, color);
	}
}

void egui_fill_rect(Bitmap *bmp, int16_t x, int16_t y, int16_t w, int16_t h, uint32_t color)
{
	egui_check_rect(x, y, w, h);

	for (int16_t xi = x; xi < x + w; xi++)
	{
		for (int16_t yi = y; yi < y + h; yi++)
		{
			egui_draw_pixel(bmp, xi, yi, color);
		}
	}
}

void egui_inverse_rect(Bitmap *bmp, int16_t x, int16_t y, int16_t w, int16_t h)
{
	egui_check_rect(x, y, w, h);

	for (int16_t xi = x; xi < x + w; xi++)
	{
		for (int16_t yi = y; yi < y + h; yi++)
		{
			egui_inverse_pixel(bmp, xi, yi);
		}
	}
}

void egui_draw_bitmap(Bitmap *bmp_panel, Bitmap *bmp_layer, int16_t x, int16_t y)
{
	for (int16_t bmp_xi = 0; bmp_xi < bmp_layer->width; bmp_xi++)
	{
		for (int16_t bmp_yi = 0; bmp_yi < bmp_layer->height; bmp_yi++)
		{
			if (bmp_layer->mode == BMP_MODE_MONO)
				egui_draw_pixel(bmp_panel, x + bmp_xi, y + bmp_yi, egui_read_pixel_mono(bmp_layer, bmp_xi, bmp_yi));
			else
				egui_draw_pixel(bmp_panel, x + bmp_xi, y + bmp_yi, egui_read_pixel_rgb565(bmp_layer, bmp_xi, bmp_yi));
		}
	}
}

void egui_draw_string(Bitmap *bmp, int16_t x, int16_t y, int16_t font_size, const char *str)
{
	if (str == NULL)
		return;

	Bitmap *ch_bmp;
	while (*str)
	{
		egui_get_char_bitmap(str, font_size, &ch_bmp);
		egui_draw_bitmap(bmp, ch_bmp, x, y);
		x += ch_bmp->width;
		str += ((*str) & 0x80) ? 2 : 1;
	}
}

void egui_cal_scale(Bitmap *bmp0, Bitmap *bmp1, float scale)
{
	bmp1->width = bmp0->width * scale;
	bmp1->height = bmp0->height * scale;

	int32_t inv_scale_fixed = (int32_t)(65536.0f / scale);

	for (int y = 0; y < bmp1->height; y++)
	{
		int src_y = (y * inv_scale_fixed) >> 16;
		if (src_y >= bmp0->height)
			src_y = bmp0->height - 1;

		for (int x = 0; x < bmp1->width; x++)
		{
			int src_x = (x * inv_scale_fixed) >> 16;
			if (src_x >= bmp0->width)
				src_x = bmp0->width - 1;

			egui_draw_pixel(bmp1, x, y, egui_read_pixel(bmp0, src_x, src_y));
		}
	}
}
void egui_cal_rotate_point(int center_x, int center_y, int *origin_x, int *origin_y, int angle)
{
	float theta = (3.1415926f / 180.0f) * angle;
	int x = *origin_x - center_x;
	int y = *origin_y - center_y;
	*origin_x = center_x + (x * cosf(theta)) - (y * sinf(theta));
	*origin_y = center_y + (x * sinf(theta)) + (y * cosf(theta));
}

uint16_t egui_rgb565(uint8_t r, uint8_t g, uint8_t b)
{
	return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
